Git命令使用总结

版本控制系统(VCS)

三大要素:版本控制、主动提交、中央仓库。

每个团队成员向中央仓库主动提交自己的改动和同步别人的改动,并在需要的时候查看和操作历史版本,这就是版本控制系统。

中央式版本控制系统(Centralized VCS)

  1. 本地没有仓库,只有一份你签出的代码和最基本的版本信息(服务器位置以及一些关于版本号的缓存等),断网则什么都干不了,如果中央服务器出了问题,所有人都没法干活了。

分布式版本控制系统(Distributed VCS)

  1. 每个团队成员都有带版本管理的本地仓库,有所有人提交的版本记录,可以离线对仓库做一些操作,有网络时再推送到远程仓库即可。它的中央仓库虽然也保存了历史版本,但这份历史版本更多的是作为团队间的同步中转站。

  2. 大多数的操作可以在本地进行,所以速度更快,而且由于无需联网,所以即使不在公司甚至没有在联网,你也可以提交代码、查看历史,从而极大地减小了开发者的网络条件和物理位置的限制。

  3. 由于可以提交到本地,所以你可以分步提交代码,把代码提交做得更细,而不是一个提交包含很多代码,难以 review 也难以回溯。因为每个人电脑里都有完整的版本库,某个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。

Git 的一些概念

本地仓库 (Local Repository)

工作区有一个隐藏目录 .git ,这个不算工作区,而是 Git 的版本库。也叫仓库(repository),你可以简单理解成一个目录,这个目录里面的所有文件都可以被 Git 管理起来,每个文件的修改和删除都能被跟踪,以便任何时刻都可以追踪历史。

工作区(Working Directory)

就是你在电脑里能看到的目录,它保存了你当前从仓库中签出(checkout)的内容。

暂存区(Stage Area)

一个汇集所有待提交的文件改动的地方。是 .git 目录下一个叫做 index 的文件,你通过 add 指令暂存的内容都会被写进这个文件里。

偏移符号

^ :在 commit 的后面加一个或多个 ^ 号,可以把 commit 往回偏移,偏移的数量是 ^ 的数量。例如:master^ 表示 master 指向的 commit 之前的那个 commitHEAD^^ 表示 HEAD 所指向的 commit 往前数两个 commit

~ :在 commit 的后面加上 ~ 号和一个数,可以把 commit 往回偏移,偏移的数量是 ~ 号后面的数。例如:HEAD~5 表示 HEAD 指向的 commit 往前数 5commit

  1. 当前 commit 在哪里,HEAD 就在哪里,这是一个永远自动指向当前 commit 的引用,所以你永远可以用 HEAD 来操作当前 commit

  2. 使用 checkoutreset 等指令手动改变当前 commit 的时候,HEAD 也会一起跟过去。每次当有新的 commit 的时候,工作目录会自动与最新的 commit 对应,HEAD 也会转而指向最新的 commit

  3. HEADGit 中一个独特的引用,它是唯一的。HEAD 除了可以指向 commit,还可以指向一个 branch,当它指向某个 branch 的时候,会通过这个 branch 来间接地指向某个 commit。另外,当 HEAD 在提交时自动向前移动的时候,它会像一个拖钩一样带着它所指向的 branch 一起移动。

branch

  1. 是一种引用,其中 masterGit 的默认 branch(俗称主 branch / 主分支)。新创建的仓库是没有任何 commit 的,但在它创建第一个 commit 时,会把 master 指向它,并把 HEAD 指向 master

  2. 当使用 git clone 时,除了从远程仓库把 .git 这个仓库目录下载到工作目录中,还会 checkout (签出) mastercheckout 的意思就是把某个 commit 作为当前 commit,把 HEAD 移动过去,并把工作目录的文件内容替换成这个 commit 所对应的内容)。所以刚 clone 的代码默认是处于 master 分支的最新提交位置内容。

  3. branch 包含了从初始 commit 到它的所有路径,而不是一条路径。并且,这些路径之间是彼此平等的。

注意:

由于 Git 中的 branch 只是一个引用,所以删除 branch 的操作也只会删掉这个引用,并不会删除任何的 commit 。(不过如果一个 commit 不在任何一个 branch 的「路径」上,或者换句话说,如果没有任何一个 branch 可以回溯到这条 commit,那么在一定时间后,它会被 Git 的回收机制删除掉。)

比如:有分㕚的不同分支,其中的一个分支有提交过,但是从来没被合并,但是分支引用却被删除了,那么这些还没被合并的 commits 将会在一定时间后被回收掉。

Git 基本命令

clone

  1. 用于将远程仓库取下来,要克隆一个仓库,首先必须知道仓库的地址,然后使用 git clone 指令。 Git 支持多种协议,包括 https ,但通过 ssh 支持的原生 git 协议速度最快。而且,把项目 clone 下来时不用 https 协议,而是用 ssh 协议,就不需要每次操作都要输入用户名和密码了。

  2. 可以加一个额外参数来手动指定本地仓库的根目录名称。
    git-practice-another 为指定目录名称
    git clone git@github.com:fandazeng/git-practice.git git-practice-another

关联本地仓库和远程仓库

  1. 要关联一个远程库,使用指令 git remote add origin git@server-name:path/repo-name.git

  2. 然后,使用指令 git push -u origin master 来推送 master 分支的所有内容(第一次推送才需要用到 -u 将本地的 master 分支和远程的 master 分支关联起来)。

  3. 此后,本地提交后就可以使用指令 git push origin master 推送最新修改。

init

初始化一个 Git 仓库,使用 git init

add

将具体的文件改动添加进暂存区(不是文件名),在 add 之后的新改动并不会自动被添加进暂存区。

  • git add -A :提交所有变化。
  • git add -u :提交被修改和被删除文件,不包括新文件。
  • git add . :提交新文件和被修改文件,不包括被删除文件。

commit

将暂存区的内容提交到仓库。 commit 的时候会进入到编辑界面:

i (插入模式) - esc (变回命令模式) - 大写两次 ZZ 退出并保存 )。

  1. 可以通过 -m 直接在后面添加本次提交的说明,这样就不会进入编辑模式了。

  2. 有时候,提交了之后才发现改错了,又不想增加一个 commit ,可以在提交时加上 --amend 参数,Git 说不会在当前 commit 上增加 commit,而是会把当前 commit 里的内容和暂存区里的内容合并起来后创建一个新的 commit,用这个新的 commit 把当前 commit 替换掉。

    git commit -m "reset last line" --amend

  3. android studio 上,可以在提交界面右边的 Amend commit 打勾。

status

用来查看工作目录当前状态的指令,可以知道哪些文件被 add 过,哪些文件被修改过等等。

log

该命令会列出你的提交历史。在 android studio 的命令行使用 git log 会处于特殊状态,可以通过双击大写的 Z 来退出。

  • git log -p :查看具体 commit 的改动细节。

  • git log --stat:查看简要统计,没有 -p 那么深入细节。

  • git log --graph:查看分支合并图,再加上 --pretty=oneline --abbrev-commit 参数,可以更简洁。

show

  • git show : 查看当前 commit 的改动内容。

  • git show <commitId> : 查看任意 commitId 的改动内容。

  • git show <tagName> : 查看 tag (tagcommitId 是绑定的)的改动内容。

  • git show <commitId> <fileName> : 查看指定 commit 中的指定文件。

diff

  • git diff : 显示工作区和暂存区之间的不同,「如果你现在把所有文件都 add ,你会向暂存区中增加哪些内容」。

  • git diff --staged/--cached : 显示暂存区和上一条提交之间的不同,[如果你立即 commit,你将会提交什么」。

  • git diff HEAD : 显示工作目录和上一条提交之间的不同,它是上面二者的内容相加。[如果你现在把所有文件都 add 然后 commit,你将会提交什么」。

注意:从来没有被 add 过 的文件并不会显示出来,因此 Git 没跟踪它们,识别不出来。

branch

  • git branch:查看分支。
  • git branch <name>:创建分支。
  • git checkout <name> 或者 git switch <name>:切换分支。
  • git checkout -b <name> 或者 git switch -c <name>:创建 + 切换分支。
  • git merge <name> :合并某分支到当前分支。
  • git branch -d <name>git branch -D <name>:删除分支。
  • git push origin -d <name> :删除远程分支。

注意:

出于安全考虑,没有被合并过的 branch 在删除时会失败(因为怕你误删掉「未完成」的 branch ),如果你确定是要删除这个 branch (例如某个未完成的功能被团队确认永久毙掉了),可以把 -d 改成 -D,就能删除了。

stash

用于临时存放工作目录的改动。

常用场景:

你在某个分支上工作,但功能还没完成,不想提交,又必须切换到其他分支进行版本发布或 bug 修复,就可以使用 git stash 来把工作现场保存起来,之后到切换回该分支进行恢复 git stash popgit stash apply

注意:

  1. 没有被跟踪的文件(即从来没有被 add 过的文件不会被 stash 起来,因为 Git 会忽略它们。如果想把这些文件也一起 stash,可以加上 -u 参数,它是 --include-untracked 的简写。

    git stash -u

  2. 每次调用 stash 都会有一条记录,可以用 git stash list 查看所有的 stash 记录,记录中有索引。

  3. 通过 git stash pop <索引> 或者 stash apply <索引> 就可以使用对应的 stashapply 可以保留 stash 空间,pop 用于恢复的同时把 stash 内容也删除。为了避免 pop 完产生奇怪的问题,所以优先使用 apply 而不是 pop

tag

发布一个版本时,我们通常先在版本库中打一个标签,这样就唯一确定了打标签时刻的版本。将来想取指定标签的版本,就是把那个打标签的时刻的历史版本取出来。 tag 就是一个让人容易记住的有意义的名字,它跟某个 commit 绑在一起。标签是指向 commit 的死指针,分支是指向 commit 的活指针。

  • git tag <tagname> :新建一个标签,默认为 HEAD ,也可以指定 commit
  • git tag -a <tagname> -m "说明信息" :新建一个带说明信息的标签。
  • git tag :查看所有标签。
  • git push origin <tagname> :推送一个本地标签。
  • git push origin --tags:推送全部未推送过的本地标签。
  • git tag -d <tagname>:删除一个本地标签。
  • git push origin :refs/tags/<tagname> :删除一个远程标签。
  • git show <tagname>: 查看标签详细信息:

注意:

  1. 标签总是和某个 commit 挂钩。如果这个 commit 都出现在不同分支,那么在这些分支上都可以看到这个标签。

  2. 默认情况下,git push 命令并不会上传标签到远程仓库,必须显式地推送标签到服务器上。 这个过程就像共享远程分支一样。 (在 studio 上,需要在 push 界面给 tags 选项打勾。)

    git push origin [tagname]

  3. git taggit stash list 都分别列出所有分支下的所有 tagstash ,而不管你此刻处于哪个分支。这样做是为了使用便利性,你可以在任意分支中选择想要的 tagstash 进行处理,而不用记得指定分支下有哪些 tagstash

push

把当前的分支上传到远程仓库,并把分支路径上的所有 commit 也一并上传。

常用场景:

push 到远程的内容有问题,但分支是独立开发的,不会影响到其他人,那可以用 --amendreset 把写错的 commit 修改或者删除掉,然后再 push 上去就好了。不过由于你在本地对已有的 commit 做了修改,这时你再 push 就会失败,因为中央仓库包含本地没有的 commit。但这个冲突不是因为同事 push 了新的提交,而是因为你刻意修改了一些内容,这个冲突是你预料到的,这时要选择强行 pushgit push origin branch1 -f

注意:

  1. 默认情况下,你用不加参数的 git push 只能上传那些之前从远端 clone 下来或者 pull 下来的分支,而如果需要 push 你本地自己创建的分支,则需要手动指定目标仓库和目标分支(并且目标分支的名称必须和本地分支完全相同)。

  2. push 只上传当前分支,并不会上传 HEAD,远程仓库的 HEAD 是永远指向默认分支(即 master)的。

  3. 也可以让远程仓库的分支名称跟本地的不一样,(其实是将本地的提交推送到远程已有的指定分支)。 git push<远程主机名> <本地分支名> : <远程分支名>

    git push origin testBranch:test

  4. git push origin <分支名> --force 是强制让你本地的 commit 覆盖远程的 commit ,要慎用。

  5. 有时候在 studiocommit 时没有记录,是因为已经用命令 commit 了,可以直接 push 到远程。

fetch

将远程仓库的最新内容拉到本地,用户在检查了以后决定是否合并到工作分支中。

  • git fetch:将远程主机的更新全部取回本地 。
  • git fetch <远程主机名> <分支名> :取回指定分支的更新,比如: git fetch origin master

取回更新后,会返回一个 FETCH_HEAD ,指的是某个 branch 在服务器上的最新状态,我们可以在本地通过它查看刚取回的更新信息: git log -p FETCH_HEAD

merge

定义: 从目标 commit 和当前 commit (即 HEAD 所指向的 commit)分叉的位置起,把目标 commit 的路径上的所有 commit 的内容一并应用到当前 commit,然后自动生成一个新的 commit。(即合并自己没有的 commit)。

冲突: 当合并不同分支时,没有修改同一部分内容,算法会自动完成。如果两个分支修改了同一部分内容,merge 的自动算法就搞不定了。这种情况称为冲突(Conflict)。

常用命令:

  • git merge branch : 将 branch 合并到当前分支。
  • git merge --abort ,取消合并操作。合并的时候发生冲突,会处于一种待解决的中间状态,此时可以取消操作。

注意:

  1. 如果 HEAD 和目标 commit 不存在分叉,但 HEAD 落后于目标 commit,那么 Git 会直接把 HEAD(以及它所指向的 branch)移动到目标 commit 。这种操作叫做 fast-forward(快速前移)。经典使用场景:本地的 master 没有新提交,而远端仓库中有同事提交了新内容到 master

  2. 合并分支时,加上--no-ff 参数就可以禁止使用 fast forward 模式合并 git merge --no-ff -m "merge dev no-ff" dev ,因为合并后的历史有分叉,就能看出来曾经做过合并,而 fast forward 方式看不出来曾经做过合并。

pull

将远程仓库的最新内容拉下来后直接合并,即: git pull = git fetch + git merge ,这样可能会产生冲突,需要手动解决。比如: git pull origin master

pull 的过程可以理解为:

git fetch origin master //从远程仓库的master分支拉取最新内容 
git merge FETCH_HEAD     //将拉取下来的最新内容合并到当前分支

reset

常用场景:

  1. 想把之前的 commit 丢弃,回到某个 commit 的位置。比如某个修改提交导致整个项目编译不了,想要撤销提交,可以用 git reset --hard 目标commit(SHA1 值) 回到某个提交位置。

常用命令:

  1. git reset --hard HEAD^HEAD^ 表示你要恢复到哪个 commit 。因为你要撤销最新的一个 commit ,所以你需要恢复到它的父 commit ,也就是 HEAD^。那么在这行之后,你的最新一条 commit 就被撤销了。

reset 的本质:

  1. reset 的本质是移动 HEAD 以及它所指向的 branch 到某一个 commit 上,它的实质行为并不是撤销。也就是说,在版本回退之后,之前的 commit 还在,可以使用 git reflog 查看原先的 commit SHA-1 码,然后 git reset --hard commitid 回退到刚才那个 commit 版本。

reset 的参数分析:

reset 指令可以重置 HEADbranch 的位置,不过在重置它们的同时,对工作目录可以选择不同的操作,而对工作目录的操作的不同,就是通过 reset 后面跟的参数来确定的。

  1. git reset --soft HEAD^ ,首先移动 HEAD 的指向,本质是撤销了上一次 commit 命令,但是保留着 add 命令。因此,上一次提交的内容会重新回到暂存区。因为当前的工作目录是在上一次提交的基础上的,所以工作目录内容不会有任何变动。此时你可以修改代码,再次 addcommit ,就能实现 git commit --amend 所要做的事情了。

  2. git reset (--mixed) HEAD^ ,首先移动 HEAD 的指向,然后清空暂存区所有的内容。本质撤销了上一次的 git addgit commit 命令。因为当前的工作目录是在上一次提交的基础上的,所以工作目录内容也不会有任何变动。这种方式比较安全,会保留工作目录的改动。

  3. git reset --hard HEAD^,首先移动 HEAD 的指向,然后清空暂存区所有的内容,最后清空了工作目录中所有的改动。此时就将 HEAD 指向的目标 commit 的内容跟暂存区的内容和工作目录的内容统一了,也就是working clean 了。这种方式会比较危险,它会把当前工作的内容丢掉。

checkout

checkout 并不止可以切换分支 。git checkout branch名 的本质,其实是把 HEAD 指向指定的 branch,然后签出这个 branch 所对应的 commit 的工作目录。所以 checkout 的目标也可以不是 branch ,而是某个 commit

git checkout HEAD^^
git checkout master~5
git checkout 78a4bc
git checkout 78a4bc^

上面这些操作都是可以的。也可以 checkout 文件 来达到撤销本地改动的目的(删除在 git 里面也是修改操作)。

场景一:在工作区修改了某个文件,还没有 add ,突然想撤销了,希望恢复到版本一开始的状态。

  1. vi readme.txt ,修改了一些内容。
  2. git restore readme.txt 或使用 git checkout -- readme.txt 来直接撤销所有的修改。

场景二: 新建了一个文件,并 commit 到了仓库。然后在本地不小心删除了该文件,想要恢复回来。

  1. rm test.txt ,在本地把 test.txt 文件删除掉。
  2. git checkout -- test.txt ,重新签出文件即可恢复 。

注意:

  1. reset 会移动 HEAD 分支的指向,而 checkout 只会移动 HEAD 自身来指向另一个分支( checkout 是带着 HEAD 走,reset 是带着 HEADbranch 一起走)。例如,假设我们有 masterdevelop 分支,我们现在在 develop 上(所以 HEAD 指向它)。 如果我们运行git reset master,那么 develop 自身现在会和 master 指向同一个提交。 而如果我们运行 git checkout master 的话,develop 不会移动,HEAD 自身会移动。 现在 HEAD 将会指向 master

revert

撤销某个 commit ,比如说某个 commit 新增了一些内容,而此时这些内容不需要了,那么有人可能会想到用 amend 来修改之前的 commit ,这样也可以。但是,如果新增的内容比较多,你要把所有的内容删掉再用 amend 提交就非常麻烦了。应该用 revert ,直接撤销指定 commit 的所有改动。

git revert commitID : 撤销指定的 commitID 。
git revert HEAD^^ :revert 的是倒数第二次提交 。
git revert HEAD : 反转最后一次提交。

注意:

  1. revert 是强行在你的 commits 链条上去除某一个环节(即让某一个提交的内容去除了),因此不是非常确定对后续的 commit 没有任何影响的话,最好还是不要乱用。

  2. revert 完成之后,把新的 commitpush 上去,这个 commit 的内容就被撤销了。它和前面所介绍的撤销方式相比,最主要的区别是,这次改动只是被「反转」了,并没有在历史中消失掉,你的历史中会存在两条 commit :一个原始 commit ,一个对它的反转 commit 。如果反转的提交后悔了,可以再次把反转的提交再反转或撤销。

  3. git reset --hard HEAD^ 是撤销当前 commit , git revert HEAD 是反转最后一次提交 ,注意两者的区别。 reset 其实是回滚到指定的分支,所以用 HEAD^ ,即回到倒数第二提交,也就是最后最新的提交去掉了。

  4. 可以用 rebase -i 的方式执行 drop 指令来丢弃某个提交。

reflog

用来查看 Git 仓库中的引用的移动记录。默认查看 HEAD 的移动历史,除此之外,也可以手动加上名称来查看其他引用的移动记录,git reflog master 查看 master 的移动记录。

常用场景:我在某个 commit 中删除了某个分支,在以后想要恢复。

  1. 可以使用 git reflog 查看删除分支的移动记录,最后找到对应的 commitId

  2. 签出对应 commitId 的版本,然后再创建分支,这个分支就是之前被删除的分支。比如:git checkout c08de9a
    git checkout -b branch1

  3. 再签回 Head 的版本,就回到之前的版本了。

注意:不再被引用直接或间接指向的 commits 会在一定时间后被 Git 回收,所以使用 reflog 来找回删除的 branch 的操作一定要及时,不然有可能会由于 commit 被回收而再也找不回来。

cherry-pick

master 分支上修复的 bug ,想要合并到当前 dev 分支,可以用 git cherry-pick <commit> 命令,把 bug 提交的修改“复制”到当前分支,避免重复劳动。

rebase

使用场景一:

git rebase 目标基础点 。

rebase 的意思是,给你的 commit 序列重新设置基础点(也就是父 commit)。展开来说就是,把你指定的 commit 以及它所在的 commit 串,以指定的目标 commit 为基础,依次重新提交一次。当两个不同的分支都有提交的话,此时将它们 merge 就会出现分叉的情况,如果不想出现这样的情况,可以不用 merge ,而用 rebase 。比如:

git checkout branch1 
git rebase master 

这两句命令的效果就是,以 masterHEAD 为基点,把 branch1 的提交全部移到 master 上。另外,在 rebase 之后,记得切回 mastermerge 一下,把 master 移到最新的 commit

git checkout master
git merge branch1

需要说明的是,rebase 是站在需要被 rebasecommit 上进行操作,这点和 merge 是不同的。

使用场景二:

rebase -i:交互式 rebase

比如,我们要修改某一个 commit ,不是最新的哦(修改最新的可以用 commit --amend),所谓「交互式 rebase」,就是在 rebase 的操作执行之前,你可以指定要 rebasecommit 链中的每一个 commit 是否需要进一步修改。

比如 : git rebase -i HEAD^^ (加上 -i 就是变为交互式的)。如果没有 -i 参数的话,这种「原地 rebase」相当于空操作,会直接结束。而在加了 -i 后,就会跳到一个新的界面。然后,选择对应的操作,操作的命令有如下种类:

pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d),可以用这个命令来删除之前的指定 commit ,在
Android studio 上是通过 skip 选项来移除的。

假如我们改成了 edit 并退出,此时就会进入修正状态,此时可以 addcommit ,在修复完成之后,就可以用rebase --continue 来继续 rebase 过程,把后面的 commit 直接应用上去。

git rebase --continue

小结:

  1. 使用方式是 git rebase -i 目标commit
  2. 在编辑界面中指定需要操作的 commits 以及操作类型;
  3. 操作完成之后用 git rebase --continue 来继续 rebase 过程。

添加文件到 Git 仓库

分两步:

  • 使用命令 git add <file>,可反复多次使用来添加多个文件;

  • 使用命令 git commit -m <message> ;

文件状态

每个文件都有 "changed / unstaged"(已修改)" ,"staged"(已修改并暂存) ,"commited"(已提交) 三种状态,以及一种特殊状态 "untracked"(未跟踪)。

详解:

  1. 文件从来没被 add 过,是处于特殊状态 "untracked"(未跟踪)。
  2. 文件被 add 了,但并没 commit ,处于 "staged"(已修改并暂存)。
  3. 文件被 commit 了 ,处于"commited"(已提交)
  4. 文件曾经被 add 过并提交过,此时文件被修改了,处于 "changed / unstaged"(已修改),此时文件需要再次被 add ,就会处于状态 2,被 commit 后就会处于状态 3 。

引用的本质

  1. 所谓「引用」(reference),其实就是一个个的字符串。这个字符串可以是一个 commitSHA-1 码(例:c08de9a…),也可以是一个 branch(例:ref: refs/heads/feature3)。

  2. Git 中的 HEAD 和每一个 branch 以及其他的引用,都是以文本文件的形式存储在本地仓库 .git 目录中,而 Git 在工作的时候,就是通过这些文本文件的内容来判断这些所谓的「引用」是指向谁的。

分支策略

首先,master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活。干活都在 dev 分支上,dev 分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把 dev 分支合并到 master 上,在 master 分支发布 1.0 版本;每个人都在 dev 分支上干活,每个人都有自己的分支,时不时地往 dev 分支上合并就可以了。

feature 分支

每添加一个新功能,最好新建一个 feature 分支,在上面开发,完成后,合并,最后,删除该 feature 分支。这样做有两个好处:代码分享和一人多任务。一人多任务的场景非常简单,根本分开的功能来创建不同的 feature 分支来开发就是多任务。

代码分享使用场景:

  1. 分开一个新功能,新建一个 feature 分支: git switch -c feature
  2. 提交了很多代码并推送到远程: git push origin feature
  3. 你的同事通过 fetchpull 把你的代码拉下来,并切换到你的功能分支。
  4. 这时候,你的同事就可以帮你 review 代码,如果有问题,你就反复修改即可。
  5. 没有问题即可合并到 master 分支了,切换到 masterpull 一下来更新最新代码,然后 merge feature 分支。
  6. 最后,删除本地和远程分支: git branch -d feature ,git push origin -d feature

注意:如果发现 feature 分支代码太烂,不准备再要了,就可以直接删除本地和远程分支,非常方便,由于分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的 -D 参数。

多人协作

当你从远程仓库克隆时,实际上 Git 自动把本地的 master 分支和远程的 master 分支对应起来了,远程仓库的默认名称是 origin

要查看远程库的信息,用 git remote ,加上参数 -v 显示更详细的信息。

本地新建的分支如果不推送到远程,对其他人就是不可见的;

从本地推送分支,使用 git push origin branch-name ,如果推送失败,先用 git pull 抓取远程的新提交;

在本地创建和远程分支对应的分支,使用 git checkout -b branch-name origin/branch-name ,本地和远程分支的名称最好一致;

建立本地分支和远程分支的关联,使用 git branch --set-upstream branch-name origin/branch-name

从远程抓取分支,使用 git pull ,如果有冲突,要先处理冲突。

配置别名

--global 参数是全局参数,也就是这些命令在这台电脑的所有 Git 仓库下都有用,针对当前用户。如果不加,那只针对当前的仓库起作用。

git config --global alias.st status (为查看状态配置别名)
git config --global alias.last 'log -1' (为显示最后一次提交信息配置别名)
git config --global alias.unstage 'reset HEAD' (为暂存区的修改撤销回工作区配置别名)
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

那么配置文件放在哪里呢:

  1. 每个仓库的 Git 配置文件都放在 .git/config 文件中
  2. 当前用户的 Git 配置文件放在用户主目录下的一个隐藏文件 .gitconfig
  3. 我们可以直接在配置文件中进行配置

基本命令操作步骤演示

创建仓库

  1. mkdir git-learning ,创建一个名为 git-learning 的文件夹。
  2. cd git-learning ,切换到该文件夹目录。
  3. pwd ,该命令可以显示当前目录,比如(/e/2020_mine_repo/git-learning)。
  4. git init ,把这个目录变成 Git 可以管理的仓库,目录下会多了一个 .git 目录,该目录是 Git 来跟踪管理版本库的。

把文件添加到版本库

  1. vi readme.txt ,然后进入编辑界面,点击 i 变成插入模式,写完内容后双击大写 Z ,就能创建一个文件,文件一定要在版本库内。
  2. git add readme.txt ,把文件添加到暂存区。
  3. git commit -m "wrote a readme file" ,把文件提交到仓库。

修改文件再添加到版本库

  1. vi readme.txt ,更改内容。
  2. git status 看看仓库当前的状态。
  3. git diff readme.txt ,查看一下修改了什么内容。
  4. git add readme.txt ,添加到暂存区。
  5. git status 再看看仓库当前的状态。
  6. git commit -m "add distributed",提交到仓库。
  7. git status 再看看仓库当前的状态。

对文件多次修改的操作演示

  1. vi readme.txt ,编辑内容。
  2. cat readme.txt ,看一下内容。
  3. git add readme.txt ,添加到暂存区。
  4. git status ,查看一下状态。
  5. vi readme.txt ,再编辑一下内容。
  6. git commit -m "git tracks changes",提交到仓库。
  7. git status ,查看一下状态。
  8. git diff HEAD -- readme.txt,查看当前文件和仓库里最新版本的区别。
  9. 再次 addcommit 第二次修改的内容。

查看文件状态的操作演示

  1. vi readme.txt ,修改内容。
  2. vi LICENSE.txt,新增一个文件。
  3. ls -ah ,可以查看一下所有的文件,-ah 是把隐藏文件都查看,默认不查看。
  4. git status ,查看一下状态。
  5. git add . ,该指令可以把所有的修改一次性 add 到暂存区。
  6. git status 再查看一下文件状态。
  7. git commit -m "understand how stage works",提交到仓库。
  8. git status 再查看一下文件状态。此时 nothing to commit, working tree clean

版本回退

  1. vi readme.txt ,更改内容,然后 addcommit
  2. git log ,查看历史记录,也可以用 git log --pretty=oneline 来输出更少的内容。
  3. git reset --hard HEAD^,回退到上一个版本。
  4. cat readme.txt ,该命令可以查看一下文件的内容,回退到了上一版本的内容。
  5. 再用 log 命令查看一下历史记录,只能看到当前的版本和以前的版本信息,最新的信息看不到了。
  6. 如果再想回到之前的版本,可以用 reflog 命令,来查看一下仓库的移动记录,默认是查看 HEAD 的。
  7. 找到之前的 commitId(比如是 9360cd0 ) ,然后 git reset --hard 9360cd0 ,回到之前的版本。

小结:

HEAD 指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id 。穿梭前,用git log 可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。